Hanye官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

[id].vue 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <div class="py-8">
  3. <div class="container-custom">
  4. <div class="mb-4">
  5. <NuxtLink to="/products" class="text-blue-600 hover:underline flex items-center">
  6. <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  7. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
  8. </svg>
  9. {{ $t('products.title') }}
  10. </NuxtLink>
  11. </div>
  12. <ErrorBoundary :error="error">
  13. <div v-if="isLoading" class="flex justify-center py-12">
  14. <!-- 加载中 -->
  15. <div class="animate-spin h-8 w-8 border-4 border-blue-500 rounded-full border-t-transparent"></div>
  16. </div>
  17. <div v-else-if="product" class="bg-white border border-gray-200 rounded-lg overflow-hidden">
  18. <div class="h-64 bg-gray-100 flex items-center justify-center">
  19. <!-- 产品图片占位符 -->
  20. <span class="text-gray-400 text-xl">{{ product.title }}</span>
  21. </div>
  22. <div class="p-8">
  23. <h1 class="text-3xl font-bold mb-4">{{ product.title }}</h1>
  24. <p class="text-gray-600 mb-6">{{ product.description }}</p>
  25. <div class="border-t border-gray-200 pt-6 mt-6">
  26. <h2 class="text-xl font-semibold mb-4">产品特点</h2>
  27. <ul class="list-disc pl-5 space-y-2">
  28. <li>高品质材料,经久耐用</li>
  29. <li>精心设计,使用便捷</li>
  30. <li>多种配置可选,满足不同需求</li>
  31. <li>售后服务完善,解决后顾之忧</li>
  32. </ul>
  33. </div>
  34. <div class="border-t border-gray-200 pt-6 mt-6">
  35. <h2 class="text-xl font-semibold mb-4">技术规格</h2>
  36. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  37. <div class="flex items-center">
  38. <span class="font-medium mr-2">尺寸:</span>
  39. <span>200 x 300 x 100 mm</span>
  40. </div>
  41. <div class="flex items-center">
  42. <span class="font-medium mr-2">重量:</span>
  43. <span>2.5 kg</span>
  44. </div>
  45. <div class="flex items-center">
  46. <span class="font-medium mr-2">材质:</span>
  47. <span>高级金属合金</span>
  48. </div>
  49. <div class="flex items-center">
  50. <span class="font-medium mr-2">颜色:</span>
  51. <span>银色、黑色、金色</span>
  52. </div>
  53. </div>
  54. </div>
  55. <div class="mt-8">
  56. <button class="btn btn-primary">
  57. 联系我们了解更多
  58. </button>
  59. </div>
  60. </div>
  61. </div>
  62. <div v-else class="bg-yellow-50 border border-yellow-200 text-yellow-800 p-4 rounded-md">
  63. 产品不存在或已被删除
  64. </div>
  65. </ErrorBoundary>
  66. </div>
  67. </div>
  68. </template>
  69. <script setup lang="ts">
  70. /**
  71. * 产品详情页面
  72. * 展示单个产品的详细信息
  73. */
  74. import { ref, onMounted } from 'vue';
  75. import { useErrorHandler } from '~/composables/useErrorHandler';
  76. // 产品接口定义
  77. interface Product {
  78. id: number;
  79. title: string;
  80. description: string;
  81. }
  82. const route = useRoute();
  83. const { error, isLoading, wrapAsync } = useErrorHandler();
  84. const product = ref<Product | null>(null);
  85. // 获取产品ID
  86. const productId = computed(() => {
  87. const id = route.params.id;
  88. return typeof id === 'string' ? parseInt(id, 10) : -1;
  89. });
  90. /**
  91. * 加载产品详情数据
  92. */
  93. async function loadProduct() {
  94. if (productId.value <= 0) {
  95. error.value = new Error('无效的产品ID');
  96. return;
  97. }
  98. await wrapAsync(async () => {
  99. // 模拟API请求延迟
  100. await new Promise(resolve => setTimeout(resolve, 500));
  101. // 模拟数据,实际项目中应从API获取
  102. const mockProducts = [
  103. {
  104. id: 1,
  105. title: '产品一',
  106. description: '这是产品一的详细描述,介绍了产品的功能、特点和适用场景。产品一是我们公司的明星产品,采用了最新技术,具有高效、稳定的特点,广泛应用于各种场景。'
  107. },
  108. {
  109. id: 2,
  110. title: '产品二',
  111. description: '这是产品二的详细描述,介绍了产品的功能、特点和适用场景。产品二是我们的经济型产品,性价比高,适合中小型企业使用。'
  112. },
  113. {
  114. id: 3,
  115. title: '产品三',
  116. description: '这是产品三的详细描述,介绍了产品的功能、特点和适用场景。产品三专为高端用户设计,提供了全方位的定制服务和专业支持。'
  117. },
  118. {
  119. id: 4,
  120. title: '产品四',
  121. description: '这是产品四的详细描述,介绍产品的功能、特点和适用场景。产品四采用模块化设计,可以根据需求进行灵活配置。'
  122. },
  123. {
  124. id: 5,
  125. title: '产品五',
  126. description: '这是产品五的详细描述,介绍产品的功能、特点和适用场景。产品五是新一代智能产品,具有自学习能力和远程控制功能。'
  127. },
  128. {
  129. id: 6,
  130. title: '产品六',
  131. description: '这是产品六的详细描述,介绍产品的功能、特点和适用场景。产品六是我们的入门级产品,简单易用,适合初学者。'
  132. }
  133. ];
  134. const foundProduct = mockProducts.find(p => p.id === productId.value);
  135. if (foundProduct) {
  136. product.value = foundProduct;
  137. } else {
  138. error.value = new Error('未找到该产品');
  139. }
  140. return product.value;
  141. });
  142. }
  143. // 页面加载时获取产品数据
  144. onMounted(() => {
  145. loadProduct();
  146. });
  147. // SEO优化
  148. useHead({
  149. title: computed(() => product.value ? `${product.value.title} - Hanye` : '产品详情 - Hanye'),
  150. meta: [
  151. {
  152. name: 'description',
  153. content: computed(() => product.value?.description || '查看产品详细信息、特点和技术规格。')
  154. }
  155. ]
  156. });
  157. </script>